package com.oem.qisda;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

    // 标识线程是否运行
    private boolean mReceiveThreadRunning = true;
    private boolean mReceiveThreadRunning2 = true;

    // 接收数据套接字
    private static DatagramSocket socket;
    // 接收命令套接字，用于调试或记录
    private static DatagramSocket cmdSocket;

    // 对应端口
    private static final int RECEIVEPORT = 10001;
    private static final int CMDPORT = 10002;
    private static final int SENDPORT = 10000;

    // 数据接收缓冲区
    private byte[] mReceive = new byte[32];
    private byte[] mReceive2 = new byte[32];

    // 原始数据的MD5值
    byte[] originalMd5;
    // 读取文件数据的MD5值
    byte[] fileMd5;

    // 服务器地址
    private static final String SERVERADDRESS = "127.0.0.1";
    // 命令保护间隔，单位为毫秒
    private int mCommandGuardInerval = 50; //ms

    private static final String TAG = "WORKOUT_DATA_HANDLER";
    BikeAPI bikeAPI = new BikeAPI();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);  // 使用主布局

        // 获取 TextView 和 Button
        TextView resultTextView = findViewById(R.id.result_text_view);

        // 启动接收线程
        try {
            socket = new DatagramSocket(RECEIVEPORT);
        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("UARTBIKE", "Socket 初始化失败");
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                final DatagramPacket dPacket = new DatagramPacket(mReceive, mReceive.length);
                while (mReceiveThreadRunning) {
                    try {
                        // 接收数据
                        socket.receive(dPacket);
                        int receiveSize = dPacket.getLength();
                        String command = bytesToHex(mReceive, receiveSize);
                        Log.d("UARTBIKE", "收到命令: " + command);

                        // 解析接收到的命令
                        String result = bikeAPI.parseCommand(command);

                        // 在UI线程中更新结果显示
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                resultTextView.setText("解析结果: " + result);
                                Toast.makeText(MainActivity.this, "解析完成：" + result, Toast.LENGTH_SHORT).show();
                            }
                        });

                        // 调用接口，得到序列化后的字节数组
                        byte[] serializedData = bikeAPI.serializeWorkoutData();

                        // 计算得到要保存的字节数组的MD5值
                        originalMd5 = calculateMD5Hash(serializedData);

                        // 保存文件到protoData.bin文件中
                        Log.d(TAG, "Serialized Data Length: " + serializedData.length);
                        saveResultToFile(serializedData);
                        boolean check = checkDeserialization();
                        if (check) {
                            Log.d(TAG, "Check Success");
                        } else {
                            Log.d(TAG, "Check Fail");
                        }
                    } catch (IOException e) {
                        Log.e("UARTBIKE", "接收数据时出错");
                        e.printStackTrace();
                    }
                    Log.d(TAG,"\n");
                }
            }
        }).start();

        // ts_uart 将命令数据发送到指定的UDP端口，用于调试或记录
        try {
            cmdSocket = new DatagramSocket(CMDPORT);
        } catch (SocketException e) {
            throw new RuntimeException(e);
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                final DatagramPacket dPacket = new DatagramPacket(mReceive2, 32);
                while (mReceiveThreadRunning2) {
                    try {
                        cmdSocket.receive(dPacket);
                    } catch (IOException e) {
                        Log.d("UARTBIKE", "Send Packet: failed");
                        e.printStackTrace();
                    }
                    int receiveSize = dPacket.getLength();
                    StringBuilder sb = new StringBuilder();
                    sb.append("Send Packet: ");
                    for (int i = 0; i < receiveSize; i++) {
                        sb.append(String.format("%02x", mReceive2[i]).toUpperCase());
                    }
                    Log.d("UARTBIKE", sb.toString());
                    Log.d("UARTBIKE", "receiveSize: " + receiveSize);
                }
            }
        }).start();

        //41 05 2A D9 04 01 08 0A
        byte[] test = new byte[]{ 0x2A, (byte)0xD9, 0x04, 0x01};
        sendCommand(test);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mReceiveThreadRunning = false;
        mReceiveThreadRunning2 = false;
        if (socket != null && !socket.isClosed()) {
            socket.close();
        }
        if (cmdSocket != null && !cmdSocket.isClosed()) {
            cmdSocket.close();
        }
    }

    private void saveResultToFile(byte[] data) {
        FileOutputStream fos = null;
        try {
            // 获取外部存储目录 /storage/emulated/0/Android/data/com.oem.qisda/files/
            // File file = new File(getExternalFilesDir(null), "protoData.bin");
            // 应用的内部文件目录  /data/data/com.oem.qisda/files
            File file = new File(getFilesDir(), "protoData.bin");

            // 使用 FileOutputStream 打开文件并写入数据
            fos = new FileOutputStream(file, false);  // true 代表以追加模式写入， false代表以覆盖模式写入

            fos.write(data);  // 写入 byte[]
            // fos.write("\n".getBytes());  // 不要写入换行符，会对二进制数据文件不必要且可能引发反序列化问题

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private byte[] readFileAsByteArray(String fileName) {
        FileInputStream fis = null;
        byte[] data = null;
        try {
            // 读取内部文件目录 /data/data/com.oem.qisda/files/
            File file = new File(getFilesDir(), fileName);

            // 创建输入流读取文件内容
            fis = new FileInputStream(file);
            data = new byte[(int) file.length()];

            // 读取文件到字节数组
            fis.read(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return data;
    }

    private boolean checkDeserialization() {
        // 从文件读取字节数组
        byte[] serializedData = readFileAsByteArray("protoData.bin");

        // 计算读取到的字节数组的MD5值
        fileMd5 = calculateMD5Hash(serializedData);

        boolean b = false;
        Log.d(TAG, "Read Data Length: " + (serializedData != null ? serializedData.length : "null"));

        if (serializedData != null && serializedData.length > 0) {
            // 调用反序列化方法
            b = bikeAPI.deserializeWorkoutData(serializedData);
            // 判断反序列化是否成功
            if (b) {
                Log.d(TAG, "Deserialize WorkoutData SUCCESS");
            } else {
                Log.d(TAG, "Deserialize WorkoutData FAILED");
            }
        } else {
            Log.d(TAG, "File is empty or could not be read.");
        }

        return b && Arrays.equals(originalMd5, fileMd5);
    }

    public static String bytesToHex(byte[] bytes, int len) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            sb.append(String.format("%02X", bytes[i]));
        }
        return sb.toString().trim();
    }

    private void sendCommand(final byte[] value) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 初始化socket
                DatagramSocket socket = null;
                try {
                    socket = new DatagramSocket();
                } catch (SocketException e) {
                    e.printStackTrace();
                }

                InetAddress serverAddress = null;
                try {
                    serverAddress = InetAddress.getByName(SERVERADDRESS);
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }

                // 创建一个包含命令数据、服务器地址和端口号的数据包
                DatagramPacket packet = new DatagramPacket(value, value.length, serverAddress, SENDPORT);
                try {
                    // 将UDP数据包发送到中间件
                    socket.send(packet);
                    Log.d("UARTBIKE", "send success!");
                    Log.d("UARTBIKE", "packet length: " + packet.getLength());
                } catch (IOException e) {
                    Log.d("UARTBIKE", "send failed!");
                    e.printStackTrace();
                }
                socket.close();

                // 等待指定的命令保护间隔，以免发送过多命令
                try {
                    Thread.sleep(mCommandGuardInerval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    // 计算 MD5 哈希值
    private byte[] calculateMD5Hash(byte[] input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            return md.digest(input);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}